package com.topsun.framework.common.core.enums;

import java.io.Serializable;
import java.util.Arrays;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @Package cloud.chenglang.frmework.enums
 * @author: 皮皮
 * @date: 2020/9/25 2:25 PM
 */
public interface BaseEnum {


    /**
     * <p> 获取枚举类对象code </p>
     *
     * @return Serializable
     */
    Serializable getCode();

    /**
     * <p> 获取枚举类对象描述信息 </p>
     *
     * @return String
     */
    String getDesc();

    /**
     * 通过code查询枚举
     *
     * @param code      code
     * @param enumClass 枚举类型
     * @param <T>       枚举
     * @return
     */
    static <T extends BaseEnum> T getByCode(Serializable code, Class<T> enumClass) {
        Objects.requireNonNull(code, "code不能为空");
        Objects.requireNonNull(enumClass, "enumClass不能为空");
        return CodeEnumCache.getByCode(code, enumClass);
    }

    /**
     * 判断code是否对应指定枚举
     *
     * @param codeEnum 枚举
     * @param code     code
     * @return
     */
    static boolean isEnum(BaseEnum codeEnum, Serializable code) {
        BaseEnum selectedCodeEnum = getByCode(code, codeEnum.getClass());
        return Objects.nonNull(selectedCodeEnum) && selectedCodeEnum.equals(codeEnum);
    }

    class CodeEnumCache {
        private final static Map<Class, Map<Serializable, BaseEnum>> CODE_ENUM_CACHE_MAP = new ConcurrentHashMap<>();

        @SuppressWarnings("unchecked")
        private static <T extends BaseEnum> T getByCode(Serializable code, Class<T> enumClass) {
            Map<Serializable, BaseEnum> map = CODE_ENUM_CACHE_MAP.get(enumClass);
            if (map != null) {
                return (T) map.get(code);
            } else {
                init(enumClass);
            }
            map = CODE_ENUM_CACHE_MAP.get(enumClass);
            return (T) map.get(code);
        }

        private static <T extends BaseEnum> void init(Class<T> enumClass) {
            synchronized (CODE_ENUM_CACHE_MAP) {
                if (CODE_ENUM_CACHE_MAP.get(enumClass) == null) {
                    Map<Serializable, BaseEnum> map = Arrays.stream(enumClass.getEnumConstants())
                            .collect(Collectors
                                    .toMap(BaseEnum::getCode, Function.identity()
                                    )
                            );
                    CODE_ENUM_CACHE_MAP.put(enumClass, map);
                }
            }
        }
    }
}
